package cn.qianxun.meta.common.core.utils;

import cn.hutool.core.util.StrUtil;
import cn.qianxun.meta.common.core.exception.base.BaseException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 时间工具类
 *
 * @author fuzhilin
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    public static String YYYY = "yyyy";

    public static String MM = "MM";

    public static String DD = "dd";

    public static String YYYY_MM = "yyyy-MM";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYYMMDDHHMM = "yyyyMMddHHmm";


    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static String HH_MM_SS = " HH:mm:ss";

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 日期转换为制定格式字符串
     *
     * @param time
     * @param format
     * @return
     */
    public static String formatDateToString(Date time, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(time);
    }

    /**
     * 字符串转换为制定格式日期
     * (注意：当你输入的日期是2014-12-21 12:12，format对应的应为yyyy-MM-dd HH:mm
     * 否则异常抛出)
     *
     * @param date
     * @param format
     * @return
     * @throws ParseException
     * @
     */
    public static Date formatStringToDate(String date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            return sdf.parse(date);
        } catch (ParseException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex.toString());
        }
    }

    /**
     * 字符串转换为制定格式日期
     * (注意：当你输入的日期是2014-12-21 12:12，format对应的应为yyyy-MM-dd HH:mm
     * 否则异常抛出)
     *
     * @param date
     * @param format
     * @return
     * @throws ParseException
     * @
     */
    public static String formatStringDateToString(String date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            return dateTime(sdf.parse(date));
        } catch (ParseException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex.toString());
        }
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static String dateTimeSecond(final Date date) {
        return parseDateToStr(YYYY_MM_DD_HH_MM_SS, date);
    }

    public static String dateTimeHour(final Date date) {
        return parseDateToStr(YYYYMMDDHHMM, date);
    }

    public static String dateTimeMinute(final Date date) {
        return parseDateToStr(HH_MM_SS, date);
    }

    public static String dateTimeYearMonth(final Date date) {
        return parseDateToStr(YYYY_MM, date);
    }

    public static String dateTimeYear(final Date date) {
        return parseDateToStr(YYYY, date);
    }

    public static String dateTimeMonth(final Date date) {
        return parseDateToStr(MM, date);
    }

    public static String dateTimeDay(final Date date) {
        return parseDateToStr(DD, date);
    }

    public static String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Long strDateToStamp(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns).getTime();
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 获取昨日时间
     *
     * @return
     */
    public static String getYesTodayDate() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1);
        Date time = cal.getTime();
        return new SimpleDateFormat(YYYY_MM_DD).format(time);
    }

    /**
     * 获取近一周
     *
     * @return
     */
    public static String getLastWeek() {
        Calendar c = Calendar.getInstance();
        //过去七天
        c.setTime(new Date());
        c.add(Calendar.DATE, -7);
        Date time = c.getTime();
        return new SimpleDateFormat(YYYY_MM_DD).format(time);
    }

    /**
     * 获取近一月
     *
     * @return
     */
    public static String getLastMonth() {
        Calendar c = Calendar.getInstance();
        //过去30天
        c.setTime(new Date());
        c.add(Calendar.DATE, -30);
        Date time = c.getTime();
        return new SimpleDateFormat(YYYY_MM_DD).format(time);
    }

    /**
     * 获取近一年
     *
     * @return
     */
    public static String getLastYear() {
        Calendar c = Calendar.getInstance();
        //过去七天
        c.setTime(new Date());
        c.add(Calendar.DATE, -365);
        Date time = c.getTime();
        return new SimpleDateFormat(YYYY_MM_DD).format(time);
    }

    /**
     * 获取当前年份的前n年的年份
     *
     * @param i
     * @return
     */
    public static String getBeforeYear(int i) {
        SimpleDateFormat formats = new SimpleDateFormat("yyyy");
        Calendar c = Calendar.getInstance();
        c.add(Calendar.YEAR, -i);
        Date date = c.getTime();
        return formats.format(date);
    }

    /**
     * 获取当前年份的前4年的年份合集(含今年)
     *
     * @return
     */
    public static List<String> getBeforeYears() {
        SimpleDateFormat formats = new SimpleDateFormat("yyyy");
        Calendar c = Calendar.getInstance();
        Date date = null;
        List<String> years = new ArrayList<>();
        c.add(Calendar.YEAR, 0);
        date = c.getTime();
        years.add(formats.format(date));
        for (int i = 1; i <= 3; i++) {
            c.add(Calendar.YEAR, -1);
            date = c.getTime();
            years.add(formats.format(date));
        }
        Collections.sort(years);
        return years;
    }


    /**
     * 获取当前月份的前n月的月份
     *
     * @param i
     * @return
     */
    public static String getBeforeMonth(int i) {
        SimpleDateFormat formats = new SimpleDateFormat("yyyy-MM");
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, -i);
        Date date = c.getTime();
        return formats.format(date);
    }

    /**
     * 获取当前月份的前12月的月份合集
     *
     * @return
     */
    public static List<String> getBeforeMonths() {
        SimpleDateFormat formats = new SimpleDateFormat("yyyy-MM");
        Calendar c = Calendar.getInstance();
        Date date = null;
        List<String> months = new ArrayList<>();
        for (int i = 12; i >= 1; i--) {
            c.add(Calendar.MONTH, -1);
            date = c.getTime();
            months.add(formats.format(date));
        }
        Collections.sort(months);
        return months;
    }

    /**
     * 获取当前自然周上一周的起始时间
     *
     * @param n
     * @return
     */
    public static Map<String, String> getWeeksDate(int n) {
        //获取当前日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar1 = Calendar.getInstance();
        Calendar calendar2 = Calendar.getInstance();
        int dayOfWeek = calendar1.get(Calendar.DAY_OF_WEEK) - 1;
        //偏移量
        int offset1 = 1 - dayOfWeek;
        int offset2 = 7 - dayOfWeek;
        calendar1.add(Calendar.DATE, offset1 - 7 * n);
        calendar2.add(Calendar.DATE, offset2 - 7 * n);
        String lastBeginDate = sdf.format(calendar1.getTime());
        String lastEndDate = sdf.format(calendar2.getTime());
        Map<String, String> weekDays = new HashMap<>();
        weekDays.put("beginDate", lastBeginDate);
        weekDays.put("endDate", lastEndDate);
        return weekDays;
    }

    /**
     * 获取当前自然周上六周的起始时间
     *
     * @return
     */
    public static List<String> getWeekDates() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        List<String> weeks = new ArrayList<>();
        for (int i = 6; i >= 1; i--) {
            Calendar calendar1 = Calendar.getInstance();
            Calendar calendar2 = Calendar.getInstance();
            int dayOfWeek = calendar1.get(Calendar.DAY_OF_WEEK) - 1;
            //偏移量
            int offset1 = 1 - dayOfWeek;
            int offset2 = 7 - dayOfWeek;
            calendar1.add(Calendar.DATE, offset1 - 7 * i);
            calendar2.add(Calendar.DATE, offset2 - 7 * i);
            String lastBeginDate = sdf.format(calendar1.getTime());
            String lastEndDate = sdf.format(calendar2.getTime());
            weeks.add(lastBeginDate + "~" + lastEndDate);
        }

        return weeks;
    }

    /**
     * 计算两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    /**
     * 计算两个时间差 天
     */
    public static Long getDateDay(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day;
    }

    /**
     * 计算两个时间差
     *
     * @param issueTime 开始时间
     * @param sealTime  结束时间
     * @return
     */
    public static String getDealDuration(Date issueTime, Date sealTime) {
        String result = "0";
        try {
            long diff = sealTime.getTime() - issueTime.getTime();//这样得到的差值是毫秒级别
            double hour = diff / (1000 * 60 * 60.0);
            result = String.format("%.2f", hour);
        } catch (Exception e) {
            throw new BaseException("转换错误！");
        }
        return result;
    }


    /**
     * 补全给定起止时间区间内的所有日期
     *
     * @param startTime
     * @param endTime
     * @param isIncludeStartTime
     * @return
     */
    public static List<String> getBetweenDates(String startTime, String endTime, boolean isIncludeStartTime) {
        List<String> result = new ArrayList<>();
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            Date d1 = new SimpleDateFormat("yyyy-MM-dd").parse(startTime);//定义起始日期
            Date d2 = new SimpleDateFormat("yyyy-MM-dd").parse(endTime);//定义结束日期  可以去当前月也可以手动写日期。
            Calendar dd = Calendar.getInstance();//定义日期实例
            dd.setTime(d1);//设置日期起始时间
            if (isIncludeStartTime) {
                result.add(format.format(d1));
            }
            while (dd.getTime().before(d2)) {//判断是否到结束日期
                dd.add(Calendar.DATE, 1);//进行当前日期加1
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String str = sdf.format(dd.getTime());
                result.add(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 获取近七天的日期(包含今天)
     *
     * @return
     */
    public static List<String> getSevenDate() {
        List<String> dateList = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 7; i++) {
            Date date = DateUtils.addDays(new Date(), -i);
            String formatDate = sdf.format(date);
            dateList.add(formatDate);
        }
        Collections.sort(dateList);
        return dateList;
    }

    /**
     * 获取近days天的日期(包含今天)
     *
     * @return
     */
    public static List<String> getDaysOfMonth(int days) {
        List<String> dateList = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i <= days; i++) {
            Date date = DateUtils.addDays(new Date(), -i);
            String formatDate = sdf.format(date);
            dateList.add(formatDate);
        }
        Collections.sort(dateList);
        return dateList;
    }

    /**
     * @return String
     * @Title:
     * @Description:获取提前或延后的日期，格式为yyyy-MM-dd HH:mm
     * @Version:1.1.0
     */
    public static String getHourDate(Date date, int hour) {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH");
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            cal.add(Calendar.HOUR_OF_DAY, -hour);
            return format.format(cal.getTime());
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * @return String
     * @Title:
     * @Description:获取提前或延后的日期，格式为yyyy-MM-dd HH:mm
     * @Version:1.1.0
     */
    public static List<String> getListHourDate(Date date, int hour) {
        List<String> hourList = new ArrayList<>();
        for (int i = 0; i <= hour; i++) {
            String hourDate = getHourDate(date, i);
            hourList.add(hourDate);
        }
        return hourList;
    }


    /**
     * 获取某个月份天数
     *
     * @param date
     * @return
     */
    public static int getDaysOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 专门针对URL的判断，请除URL外其他模块勿用，传递URL的后缀，不需要传递其前缀
     *
     * @param strDate
     * @return
     */
    public static boolean isDate4Url(String strDate) {
        if (StrUtil.isEmpty(strDate)) {
            return false;
        }
        strDate = strDate.substring(0, strDate.lastIndexOf("/"));
        String regEx = "[^0-9]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(strDate);
        String trim = m.replaceAll("").trim();
        //日期格式yyyy
        String date_y = "^(\\d{4})$";
        Pattern py = Pattern.compile(date_y);
        Matcher my = py.matcher(trim);
        if (my.matches()) {
            return true;
        }
        //日期格式yyyy-mm
        String date_ym = "^(\\d{4})(0\\d{1}|1[0-2])$";
        Pattern pym = Pattern.compile(date_ym);
        Matcher mym = pym.matcher(trim);
        if (mym.matches()) {
            return true;
        }
        //日期格式yyyy-mm-dd
        String date_ymd = "^(\\d{4})(0\\d{1}|1[0-2])(0\\d{1}|[12]\\d{1}|3[01])$";
        Pattern pymd = Pattern.compile(date_ymd);
        Matcher mymd = pymd.matcher(trim);
        if (mymd.matches()) {
            return true;
        }
        return false;
    }

    /**
     * 格式化时区时间
     *
     * @param zoneTime
     * @return
     */
    public static Date formatZoneTime(String zoneTime) {
        String tzId = TimeZone.getDefault().getID();
        TimeZone tz = TimeZone.getTimeZone(tzId);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        formatter.setTimeZone(tz);
        Date date = null;
        try {
            date = formatter.parse(zoneTime);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 获取年月日中文格式
     */
    public static String chineseTime(Date date) {
        return dateTimeYear(date) + "年" + dateTimeMonth(date) + "月" + dateTimeDay(date) + "日";
    }
}
